use anyhow::Result;

use argon2::{
    Argon2, password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng}
};
use chat_core::User;
use serde::{Serialize,Deserialize};
use tracing::info;
use utoipa::{ToSchema};

use crate::{AppState, error::AppError};


#[derive(Debug,Serialize,Deserialize,ToSchema,Clone)]
pub struct CreateUser{
    pub fullname: String,

    pub email: String,

    pub workspace: String,

    pub password: String,
}


#[derive(Debug,Serialize,Deserialize,ToSchema,Clone)]
pub struct SignUser {
    pub email: String,

    pub password: String,
}

impl AppState {
    pub async fn find_user_by_email(&self,email: &str) ->Result<Option<User>>{
        //query user by email from db here
        let user = sqlx::query_as(
            "SELECT id,ws_id,fullname,email,created_at FROM users WHERE email = $1"
        ).bind(email)
        .fetch_optional(&self.pool)
        .await?;
        Ok(user)
    }

    pub async fn find_user_by_id(&self,id: i64) ->Result<Option<User>>{
        let user = sqlx::query_as(
            "SELECT id,ws_id,fullname,email,created_at FROM users WHERE id = $1"
        ).bind(id)
        .fetch_optional(&self.pool)
        .await?;
        Ok(user)
    }

    pub async fn create_user(&self,input: &CreateUser) -> Result<User,AppError>{
        let user = self.find_user_by_email(&input.email).await?;
        if user.is_some() {
            return Err(AppError::EmailAlreadyExists(input.email.clone()));
        }
                let ws = match self.find_workspace_by_name(&input.workspace).await? {
            Some(ws) => ws,
            None => self.create_workspace(&input.workspace, 0).await?,
        };

        let password_hash = hash_password(&input.password)?;
        let mut user: User = sqlx::query_as(
            "INSERT INTO users (ws_id,fullname,email,password_hash) VALUES ($1,$2,$3,$4) RETURNING id,ws_id,fullname,email,created_at"
        ).bind(ws.id)
        .bind(&input.fullname)
        .bind(&input.email)
        .bind(&password_hash)
        .fetch_one(&self.pool)
        .await?;

    user.ws_name = ws.name.clone();
    // if ws.owner_id == 0 {
    //     self
    // }
        user.ws_id = ws.id;
                if ws.owner_id == 0 {
            self.update_workspace_owner(ws.id as _, user.id as _)
                .await?;
        }
        Ok(user)
    }

    pub async fn verify_user(&self, input: &SignUser) -> Result<Option<User>, AppError> {
    let user: Option<User> = sqlx::query_as(
        "SELECT id, ws_id, fullname, email,password_hash as password_hash, created_at FROM users WHERE email = $1",
    )
    .bind(&input.email)
    .fetch_optional(&self.pool)
    .await?;

    info!("User fetched for email {}: {:?}", input.email, user.clone());
    if let Some(mut user) = user {

        let password_hash = user.password_hash.clone();
        info!("Password hash for {}: {:?}", input.email, password_hash);
        let is_valid = verify_password(&input.password, &password_hash.unwrap_or_default())?;
        info!("Password verification for {}: {}", input.email, is_valid);
        
        if is_valid {
            // load ws_name, ws should exist    
            let ws = self.find_workspace_by_id(user.ws_id as _).await?.unwrap();
            user.ws_name = ws.name;
            Ok(Some(user))
        } else {
            Ok(None)
        }
    } else {
        // 用户不存在的情况
        info!("No user found for email: {}", input.email);
        Ok(None)
    }
}
    pub async fn fetch_chat_users(&self,ws_id: u64) ->Result<Vec<User>,AppError>{
        let users = sqlx::query_as(
            r#"
            SELECT id,fullname,email,ws_id,password_hash,created_at
            FROM users
            WHERE ws_id = $1
            "#
        )
        .bind(ws_id as i64)
        .fetch_all(&self.pool)
        .await?;
        Ok(users)
    }

    pub async fn fetch_users_by_ids(&self,ids: &[i64]) ->Result<Vec<User>,AppError>{
        let users = sqlx::query_as(
            r#"
            SELECT id,fullname,email,ws_id,password_hash,created_at
            FROM users
            WHERE id = ANY($1)
            "#
        )
        .bind(ids)
        .fetch_all(&self.pool)
        .await?;
        Ok(users)
    }
}


fn hash_password(password: &str) -> Result<String, AppError> {
    let salt = SaltString::generate(&mut OsRng);

    // Argon2 with default params (Argon2id v19)
    let argon2 = Argon2::default();

    // Hash password to PHC string ($argon2id$v=19$...)
    let password_hash = argon2
        .hash_password(password.as_bytes(), &salt)
        .map(|h| h.to_string())?;

    Ok(password_hash)
}

fn verify_password(password: &str, password_hash: &str) -> Result<bool, AppError> {
    let argon2 = Argon2::default();
    let password_hash = PasswordHash::new(password_hash)?;

    // Verify password
    let is_valid = argon2
        .verify_password(password.as_bytes(), &password_hash)
        .is_ok();

    Ok(is_valid)
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hash_and_verify_password() {
        let password = "123456";
        let password_hash = "$argon2id$v=19$m=19456,t=2,p=1$cShCqhPjdvWtL/TcCyjG9w$BjwgIdEBin7yuSfEDuynSCWwTETw54vv0m5Rp0n28sU";
        let is_valid = verify_password(password, password_hash).unwrap();
        assert!(is_valid);
    }
}